`timescale 1ns / 1ps

// pipe data & vaild & ready
module hs_fifo2
#(
    parameter WIDTH = 16
)
(
    input   clk,
    input   rst,
    
    output  i_rdy,
    input   i_vld,
    input   [WIDTH-1 : 0]   i_data,
    
    input   o_rdy,
    output  o_vld,
    output  [WIDTH-1 : 0]   o_data
);

wire m_vld, m_rdy;
wire [WIDTH -1 : 0] m_data;

hs_skid #(
    .WIDTH  ( WIDTH )
) inst_skid (
    .clk                     ( clk      ),
    .rst                     ( rst      ),

    .i_rdy                   ( i_rdy    ),
    .i_vld                   ( i_vld    ),
    .i_data                  ( i_data   ),

    .o_rdy                   ( m_rdy    ),
    .o_vld                   ( m_vld    ),
    .o_data                  ( m_data   )
);

hs_pipe #(
    .WIDTH  ( WIDTH )
) inst_pipe (
    .clk                     ( clk      ),
    .rst                     ( rst      ),

    .i_rdy                   ( m_rdy    ),
    .i_vld                   ( m_vld    ),
    .i_data                  ( m_data   ),

    .o_rdy                   ( o_rdy    ),
    .o_vld                   ( o_vld    ),
    .o_data                  ( o_data   )
);

endmodule

// pipe data & vaild
module hs_pipe
#(
    parameter WIDTH = 16
)
(
    input   clk,
    input   rst,
    
    input   i_vld,
    output  i_rdy,
    input   [WIDTH-1 : 0]   i_data,
    
    output  o_vld,
    input   o_rdy,
    output  [WIDTH-1 : 0]   o_data
);

reg     [WIDTH-1 : 0]   pipe_data;
reg     pipe_vld;
wire    enable;

assign i_rdy = enable;
assign o_vld = pipe_vld;
assign o_data = pipe_data;

assign enable = ~pipe_vld | o_rdy;

always @ (posedge clk)
begin
    if (rst)
    begin
        pipe_vld <= 1'b0; 
    end
    else
    begin
        if (enable)
        begin
            pipe_vld  <= i_vld;
        end
        if (enable & i_vld)
        begin
            pipe_data <= i_data;
        end
    end
end

endmodule


// pipe ready
module hs_skid
#(
    parameter WIDTH = 16
)
(
    input   clk,
    input   rst,
    
    input   i_vld,
    output  i_rdy,
    input   [WIDTH-1 : 0]   i_data,
    
    output  o_vld,
    input   o_rdy,
    output  [WIDTH-1 : 0]   o_data
);

reg     [WIDTH-1 : 0]   skid_data;
reg     skid_vld;
reg     pipe_rdy;
wire    enable;

assign i_rdy  = pipe_rdy;
assign o_vld  = pipe_rdy ? i_vld  : skid_vld;
assign o_data = pipe_rdy ? i_data : skid_data;

assign enable = ~o_rdy & pipe_rdy;

always @ (posedge clk)
begin
    if (rst)
    begin
        skid_vld <= 1'b0;
        pipe_rdy <= 1'b0;
    end
    else
    begin
        pipe_rdy <= o_rdy;
        if (enable)
        begin
            skid_vld  <= i_vld;
        end
        if (enable & i_vld)
        begin
            skid_data <= i_data;
        end
    end
end

endmodule


// hs signal 2 in 1 out
module hs_combine
#(
    parameter WIDTH0 = 16,
    parameter WIDTH1 = 16
)
(
    input   clk,
    input   rst,
    
    input   i0_vld,
    output  i0_rdy,
    input   [WIDTH0-1 : 0]   i0_data,

    input   i1_vld,
    output  i1_rdy,
    input   [WIDTH1-1 : 0]   i1_data,
    
    output  o_vld,
    input   o_rdy,
    output  [WIDTH0-1 : 0]   o0_data,
    output  [WIDTH1-1 : 0]   o1_data
);

reg     [WIDTH0-1 : 0]   pipe0_data;
reg     [WIDTH1-1 : 0]   pipe1_data;
reg     pipe_vld;

wire    in_rdy;
wire    in_vld;
wire    enable;

assign o_vld = pipe_vld;
assign i0_rdy = in_rdy;
assign i1_rdy = in_rdy;
assign o0_data = pipe0_data;
assign o1_data = pipe1_data;

assign in_rdy = in_vld & enable;
assign in_vld = i0_vld & i1_vld;
assign enable = ~pipe_vld | o_rdy;

always @ (posedge clk)
begin
    if (rst)
    begin
        pipe_vld <= 1'b0;
    end
    else
    begin
        if (enable)
        begin
            pipe_vld  <= in_vld;
        end
        if (in_rdy)
        begin
            pipe0_data <= i0_data;
            pipe1_data <= i1_data;
        end
    end
end

endmodule
